package org.convallaria.system.biz.service.impl;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.convallaria.infrastruct.ce.util.CacheKeyGenerator;
import org.convallaria.infrastruct.ce.util.CacheUtils;
import org.convallaria.infrastruct.ce.util.RedisCacheUtils;
import org.convallaria.system.biz.service.TokenBlacklistService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Set;

/**
 * Token黑名单服务实现
 * 
 * @author convallaria
 * @since 1.0.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class TokenBlacklistServiceImpl implements TokenBlacklistService {

    private final RedisTemplate<String, String> redisTemplate;
    private final RedisCacheUtils redisCacheUtils;
    private final CacheUtils cacheUtils;
    private final CacheKeyGenerator keyGenerator;

    // 缓存配置常量
    private static final String BLACKLIST_CACHE_NAME = "token:blacklist";
    private static final String STATISTICS_CACHE_NAME = "token:blacklist:stats";
    
    // 键前缀
    private static final String BLACKLIST_PREFIX = "blacklist:";
    private static final String STATISTICS_KEY = "statistics";
    
    // 默认配置
    private static final int DEFAULT_EXPIRE_HOURS = 24;

    @Override
    public void addToBlacklist(String token, String reason, LocalDateTime expireTime) {
        if (token == null || token.trim().isEmpty()) {
            return;
        }

        try {
            // 生成缓存键
            String blacklistKey = keyGenerator.generateKey(BLACKLIST_PREFIX, token);
            
            // 构建黑名单信息
            BlacklistInfo blacklistInfo = BlacklistInfo.builder()
                    .token(token)
                    .reason(reason)
                    .addTime(LocalDateTime.now())
                    .expireTime(expireTime)
                    .build();
            
            // 使用RedisCacheUtils存储黑名单信息
            Duration duration = Duration.between(LocalDateTime.now(), expireTime);
            redisCacheUtils.set(blacklistKey, blacklistInfo, duration);
            
            // 使用CacheUtils存储到通用缓存
            cacheUtils.put(BLACKLIST_CACHE_NAME, blacklistKey, blacklistInfo);

            // 更新统计信息
            updateStatistics(1, 0);

            log.info("Token已加入黑名单: {}, 原因: {}, 过期时间: {}", token, reason, 
                    expireTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));

        } catch (Exception e) {
            log.error("添加Token到黑名单失败: token={}", token, e);
        }
    }

    @Override
    public void addToBlacklist(String token, String reason) {
        LocalDateTime expireTime = LocalDateTime.now().plusHours(DEFAULT_EXPIRE_HOURS);
        addToBlacklist(token, reason, expireTime);
    }

    @Override
    public boolean isBlacklisted(String token) {
        if (token == null || token.trim().isEmpty()) {
            return false;
        }

        try {
            String blacklistKey = keyGenerator.generateKey(BLACKLIST_PREFIX, token);
            return redisCacheUtils.exists(blacklistKey);
        } catch (Exception e) {
            log.error("检查Token黑名单状态失败: token={}", token, e);
            return false;
        }
    }

    @Override
    public void removeFromBlacklist(String token) {
        if (token == null || token.trim().isEmpty()) {
            return;
        }

        try {
            String blacklistKey = keyGenerator.generateKey(BLACKLIST_PREFIX, token);
            
            // 删除Redis中的黑名单信息
            redisCacheUtils.delete(blacklistKey);
            
            // 删除通用缓存中的信息
            cacheUtils.evict(BLACKLIST_CACHE_NAME, blacklistKey);
            
            // 更新统计信息
            updateStatistics(-1, 0);
            
            log.info("Token已从黑名单中移除: {}", token);
        } catch (Exception e) {
            log.error("从黑名单中移除Token失败: token={}", token, e);
        }
    }

    @Override
    public void addToBlacklist(List<String> tokens, String reason) {
        if (tokens == null || tokens.isEmpty()) {
            return;
        }

        for (String token : tokens) {
            addToBlacklist(token, reason);
        }
    }

    @Override
    @Scheduled(fixedRate = 3600000) // 每小时执行一次
    public void cleanExpiredTokens() {
        try {
            String pattern = BLACKLIST_PREFIX + "*";
            Set<String> keys = redisCacheUtils.keys(pattern);
            
            if (keys != null && !keys.isEmpty()) {
                int cleanedCount = 0;
                LocalDateTime now = LocalDateTime.now();
                
                for (String key : keys) {
                    try {
                        BlacklistInfo blacklistInfo = redisCacheUtils.get(key, BlacklistInfo.class);
                        if (blacklistInfo != null && now.isAfter(blacklistInfo.getExpireTime())) {
                            // 删除过期的黑名单信息
                            redisCacheUtils.delete(key);
                            cacheUtils.evict(BLACKLIST_CACHE_NAME, key);
                            cleanedCount++;
                        }
                    } catch (Exception e) {
                        log.warn("清理黑名单Token失败: key={}", key, e);
                        // 如果获取失败，直接删除
                        redisCacheUtils.delete(key);
                        cacheUtils.evict(BLACKLIST_CACHE_NAME, key);
                        cleanedCount++;
                    }
                }
                
                if (cleanedCount > 0) {
                    updateStatistics(0, cleanedCount);
                    log.info("清理过期黑名单Token: {} 个", cleanedCount);
                }
            }
        } catch (Exception e) {
            log.error("清理过期黑名单Token失败", e);
        }
    }

    @Override
    public BlacklistStatistics getStatistics() {
        try {
            String statsKey = keyGenerator.generateKey(STATISTICS_KEY);
            
            // 从Redis获取统计信息
            BlacklistStatistics stats = redisCacheUtils.get(statsKey, BlacklistStatistics.class);
            
            if (stats == null) {
                // 如果Redis中没有，从通用缓存获取
                stats = cacheUtils.get(STATISTICS_CACHE_NAME, statsKey, BlacklistStatistics.class);
            }
            
            if (stats == null) {
                // 如果都没有，重新计算
                stats = calculateStatistics();
                // 存储到缓存
                redisCacheUtils.set(statsKey, stats, Duration.ofDays(1));
                cacheUtils.put(STATISTICS_CACHE_NAME, statsKey, stats);
            }
            
            return stats;
        } catch (Exception e) {
            log.error("获取黑名单统计信息失败", e);
            return BlacklistStatistics.empty();
        }
    }

    /**
     * 更新统计信息
     */
    private void updateStatistics(int activeDelta, int expiredDelta) {
        try {
            String statsKey = keyGenerator.generateKey(STATISTICS_KEY);
            
            // 获取当前统计信息
            BlacklistStatistics currentStats = redisCacheUtils.get(statsKey, BlacklistStatistics.class);
            if (currentStats == null) {
                currentStats = BlacklistStatistics.empty();
            }
            
            // 更新统计信息
            long newTotalCount = Math.max(0, currentStats.getTotalCount() + activeDelta + expiredDelta);
            long newActiveCount = Math.max(0, currentStats.getActiveCount() + activeDelta);
            long newExpiredCount = Math.max(0, currentStats.getExpiredCount() + expiredDelta);
            LocalDateTime lastCleanTime = (expiredDelta > 0) ? LocalDateTime.now() : currentStats.getLastCleanTime();
            
            BlacklistStatistics newStats = BlacklistStatistics.builder()
                    .totalCount(newTotalCount)
                    .activeCount(newActiveCount)
                    .expiredCount(newExpiredCount)
                    .lastCleanTime(lastCleanTime)
                    .build();
            
            // 存储更新后的统计信息
            redisCacheUtils.set(statsKey, newStats, Duration.ofDays(1));
            cacheUtils.put(STATISTICS_CACHE_NAME, statsKey, newStats);
            
        } catch (Exception e) {
            log.error("更新黑名单统计信息失败", e);
        }
    }

    /**
     * 计算统计信息
     */
    private BlacklistStatistics calculateStatistics() {
        try {
            String pattern = BLACKLIST_PREFIX + "*";
            Set<String> keys = redisCacheUtils.keys(pattern);
            
            long activeCount = 0;
            long expiredCount = 0;
            LocalDateTime now = LocalDateTime.now();
            
            if (keys != null) {
                for (String key : keys) {
                    try {
                        BlacklistInfo blacklistInfo = redisCacheUtils.get(key, BlacklistInfo.class);
                        if (blacklistInfo != null) {
                            if (now.isAfter(blacklistInfo.getExpireTime())) {
                                expiredCount++;
                            } else {
                                activeCount++;
                            }
                        }
                    } catch (Exception e) {
                        log.debug("计算统计信息时跳过无效key: {}", key);
                    }
                }
            }
            
            return BlacklistStatistics.builder()
                    .totalCount(activeCount + expiredCount)
                    .activeCount(activeCount)
                    .expiredCount(expiredCount)
                    .lastCleanTime(LocalDateTime.now())
                    .build();
                    
        } catch (Exception e) {
            log.error("计算黑名单统计信息失败", e);
            return BlacklistStatistics.empty();
        }
    }

    /**
     * 黑名单信息
     */
    @lombok.Data
    @lombok.Builder
    public static class BlacklistInfo {
        private String token;
        private String reason;
        private LocalDateTime addTime;
        private LocalDateTime expireTime;
    }
}
